﻿#include "CircleGraphicsItem.h"
#include "GraphicsDef.h"
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QFocusEvent>

namespace OfUI {

	CircleGraphicsItem::CircleGraphicsItem(QGraphicsItem *parent)
		: BaseGraphicsItem(parent)
		, m_bAdding(true)
		, m_eSelType(SELECT_NONE)
	{
		setType(ItemType::TWO_POINT_CIRCLE);
	}

	CircleGraphicsItem::~CircleGraphicsItem()
	{
	}

	void CircleGraphicsItem::setRect(const QRectF & rect)
	{
		prepareGeometryChange();
		m_rect = rect;
		emit signalDataChanged();
	}

	QRectF CircleGraphicsItem::getRect() const
	{
		return m_rect;
	}

	bool CircleGraphicsItem::isAdding() const
	{
		return m_bAdding;
	}

	bool CircleGraphicsItem::isEditing() const
	{
		return SELECT_NONE != m_eSelType || m_bAdding;
	}

	bool CircleGraphicsItem::isVaild() const
	{
		if (qAbs(m_rect.width()) > 1e-6 && qAbs(m_rect.height()) > 1e-6)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	BaseGraphicsItem::MouseDragType CircleGraphicsItem::select(const QPointF & point, int * selType) const
	{
		MouseDragType eDragType = DRAG_NONE;
		SelectType eSelectType = SELECT_NONE;
		QMap<SelectType, QPointF> dragPointMap = getDragSelectPoint(m_rect);

		for (auto iter = dragPointMap.begin(); iter != dragPointMap.end(); ++iter)
		{
			double dDistance = ((iter.key() == SELECT_CENTER) ? 5 : 3) / getViewScale();
			QPointF dragPoint = iter.value();
			QRectF pointRect(dragPoint - QPointF(dDistance, dDistance), dragPoint + QPointF(dDistance, dDistance));

			if (pointRect.contains(point))
			{
				eDragType = DRAG_EDIT;
				eSelectType = iter.key();
				break;
			}
		}

		if (SELECT_NONE == eSelectType && m_rect.contains(point))
		{
			eDragType = DRAG_MOVE;
			eSelectType = SELECT_ALL;
		}

		if (selType)
		{
			*selType = eSelectType;
		}
		return eDragType;
	}

	void CircleGraphicsItem::cursor(const QPointF & point, QCursor & cursor) const
	{
		int nType;
		if (BaseGraphicsItem::DRAG_NONE != select(point, &nType))
		{
			switch (nType)
			{
			case SELECT_LEFT:
			case SELECT_RIGHT:
				cursor = Qt::SizeHorCursor;
				break;
			case SELECT_TOP:
			case SELECT_BOTTOM:
				cursor = Qt::SizeVerCursor;
				break;
			case SELECT_CENTER:
			case SELECT_ALL:
				cursor = Qt::SizeAllCursor;
				break;
			default:
				break;
			}
		}
	}

	void CircleGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent * event)
	{
		if (Qt::LeftButton == event->button())
		{
			m_pressMousePos = event->scenePos();

			if (isAdding())
			{
				setLine(QLineF(m_pressMousePos, m_pressMousePos));
				return;
			}
			else
			{
				int nType;
				if (BaseGraphicsItem::DRAG_NONE != select(m_pressMousePos, &nType))
				{
					m_eSelType = static_cast<SelectType>(nType);
					return;
				}
			}
		}
		QFocusEvent focusEvent(QEvent::FocusOut, Qt::OtherFocusReason);
		focusOutEvent(&focusEvent);
	}

	void CircleGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
	{
		QFocusEvent focusEvent(QEvent::FocusOut, Qt::OtherFocusReason);
		focusOutEvent(&focusEvent);
	}

	void CircleGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent * event)
	{
		if (SELECT_NONE == m_eSelType && !m_bAdding)
			return;

		QPointF mousePos = event->scenePos();
		QRectF sceneRect = scene()->sceneRect();
		// 保证鼠标在场景内
		mousePos.setX(qMin(mousePos.rx(), sceneRect.right()));
		mousePos.setY(qMin(mousePos.ry(), sceneRect.bottom()));
		mousePos.setX(qMax(mousePos.rx(), sceneRect.left()));
		mousePos.setY(qMax(mousePos.ry(), sceneRect.top()));

		if (m_bAdding)
		{
			setLine(QLineF(m_line.p1(), mousePos));
		}
		else
		{
			QRectF newRect = m_rect;

			if (SELECT_TOP == m_eSelType)
			{
				QPointF bottomCenter(newRect.center().x(), newRect.bottom());
				QPointF topCenter(newRect.center().x(), mousePos.y());
				double dCricleRadius = (bottomCenter.y() - topCenter.y()) / 2;
				newRect = QRectF(topCenter - QPointF(dCricleRadius, 0), bottomCenter + QPointF(dCricleRadius, 0));
			}
			else if (SELECT_LEFT == m_eSelType)
			{
				QPointF leftCenter(mousePos.x(), newRect.center().y());
				QPointF rightCenter(newRect.right(), newRect.center().y());
				double dCricleRadius = (rightCenter.x() - leftCenter.x()) / 2;
				newRect = QRectF(leftCenter - QPointF(0, dCricleRadius), rightCenter + QPointF(0, dCricleRadius));
			}
			else if (SELECT_RIGHT == m_eSelType)
			{
				QPointF leftCenter(newRect.left(), newRect.center().y());
				QPointF rightCenter(mousePos.x(), newRect.center().y());
				double dCricleRadius = (rightCenter.x() - leftCenter.x()) / 2;
				newRect = QRectF(leftCenter - QPointF(0, dCricleRadius), rightCenter + QPointF(0, dCricleRadius));
			}
			else if (SELECT_BOTTOM == m_eSelType)
			{
				QPointF bottomCenter(newRect.center().x(), mousePos.y());
				QPointF topCenter(newRect.center().x(), newRect.top());
				double dCricleRadius = (bottomCenter.y() - topCenter.y()) / 2;
				newRect = QRectF(topCenter - QPointF(dCricleRadius, 0), bottomCenter + QPointF(dCricleRadius, 0));
			}
			else if (SELECT_CENTER == m_eSelType || SELECT_ALL == m_eSelType)
			{
				newRect.translate(mousePos - m_pressMousePos);
				m_pressMousePos = mousePos;
			}
			setRect(newRect);
		}
	}

	void CircleGraphicsItem::focusOutEvent(QFocusEvent * event)
	{
		Q_UNUSED(event);
		if (isAdding())
		{
			emit signalAddFinished();
			m_bAdding = false;
		}
		m_eSelType = SELECT_NONE;
	}

	QRectF CircleGraphicsItem::boundingRect() const
	{
		double dLeft = qMin(m_rect.left(), m_rect.right());
		double dRight = qMax(m_rect.left(), m_rect.right());
		double dTop = qMin(m_rect.top(), m_rect.bottom());
		double dBottom = qMax(m_rect.top(), m_rect.bottom());
		QRectF realRect(QPointF(dLeft, dTop), QPointF(dRight, dBottom));
		return realRect.adjusted(-5, -5, 5, 5);
	}

	void CircleGraphicsItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
	{
		Q_UNUSED(option);
		Q_UNUSED(widget);

		if (this->scene())
		{
			painter->save();
			painter->setRenderHint(QPainter::Antialiasing);

			// 通过反向缩放painter实现矢量绘制效果
			double dViewScale = getViewScale();
			painter->scale(1 / dViewScale, 1 / dViewScale);

			// painter缩小，则绘制坐标放大，保证显示位置不变
			QRectF scaleRect = QRectF(m_rect.topLeft() * dViewScale, m_rect.bottomRight() * dViewScale);

			// 绘制圆
			painter->setPen(getPen(getState()));
			painter->drawEllipse(scaleRect);

			// 绘制中心十字
			double dRaduis = 5;
			if (scaleRect.width() >= 2 * dRaduis && scaleRect.height() >= 2 * dRaduis)
			{
				painter->drawLine(scaleRect.center() + QPointF(-dRaduis, 0), scaleRect.center() + QPointF(dRaduis, 0));
				painter->drawLine(scaleRect.center() + QPointF(0, -dRaduis), scaleRect.center() + QPointF(0, dRaduis));
			}

			// 绘制选择点
			if (STATE_SELECT == getState())
			{
				QPointF radiusPoint(3, 3);
				QMap<SelectType, QPointF> dragPointMap = getDragSelectPoint(scaleRect);
				dragPointMap.remove(SELECT_CENTER);

				for (auto iter = dragPointMap.begin(); iter != dragPointMap.end(); ++iter)
				{
					QPointF point = iter.value();
					painter->drawRect(QRectF(point - radiusPoint, point + radiusPoint));
				}
			}
			painter->restore();
		}
	}

	void CircleGraphicsItem::setLine(const QLineF & line)
	{
		m_line = line;
		// 计算圆外接正方形
		QPointF center = m_line.center();
		double dRadius = m_line.length() / 2;
		QRectF rect = QRectF(center - QPointF(dRadius, dRadius), center + QPointF(dRadius, dRadius));
		setRect(rect);
	}

	QMap<CircleGraphicsItem::SelectType, QPointF> CircleGraphicsItem::getDragSelectPoint(const QRectF & rect) const
	{
		// 中心点
		QPointF centerPoint = rect.center();
		// 选择点(相对位置)
		QPointF topLeftPoint = rect.topLeft();
		QPointF topRightPoint = rect.topRight();
		QPointF bottomLeftPoint = rect.bottomLeft();
		QPointF bottomRightPoint = rect.bottomRight();
		QPointF topPoint = QPointF(centerPoint.rx(), topLeftPoint.ry());
		QPointF leftPoint = QPointF(topLeftPoint.rx(), centerPoint.ry());
		QPointF rightPoint = QPointF(topRightPoint.rx(), centerPoint.ry());
		QPointF bottomPoint = QPointF(centerPoint.rx(), bottomLeftPoint.ry());

		QMap<SelectType, QPointF> map;
		map.insert(SELECT_TOP, topPoint);
		map.insert(SELECT_LEFT, leftPoint);
		map.insert(SELECT_RIGHT, rightPoint);
		map.insert(SELECT_BOTTOM, bottomPoint);
		map.insert(SELECT_CENTER, centerPoint);
		return map;
	}

}
